package org.pyq.agent;

import com.alibaba.bytekit.asm.MethodProcessor;
import com.alibaba.bytekit.asm.inst.impl.InstrumentImpl;
import com.alibaba.bytekit.asm.interceptor.InterceptorProcessor;
import com.alibaba.bytekit.asm.interceptor.parser.DefaultInterceptorClassParser;
import com.alibaba.bytekit.utils.AgentUtils;
import com.alibaba.bytekit.utils.AsmUtils;
import com.alibaba.bytekit.utils.Decompiler;
import com.alibaba.bytekit.utils.VerifyUtils;
import com.alibaba.deps.org.objectweb.asm.Type;
import com.alibaba.deps.org.objectweb.asm.tree.ClassNode;
import com.alibaba.deps.org.objectweb.asm.tree.MethodNode;
import javassist.*;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.FieldInfo;
import javassist.bytecode.LineNumberAttribute;
import javassist.bytecode.MethodInfo;
import org.pyq.agent.bytekit.MyInstrument;
import org.pyq.agent.bytekit.SampleInterceptor;

import java.io.IOException;
import java.io.RandomAccessFile;
import java.lang.instrument.*;
import java.security.ProtectionDomain;
import java.util.List;

/**
 * @author pengYongQiang
 * @date 2020/8/30 14:03
 */
public class AgentMain implements ClassFileTransformer {
    static ClassNode apmClassNode;
    static ClassNode originClassNode;

    static ClassNode targetClassNode;

    public static void premain(String agentArgs, Instrumentation inst) throws Exception {
        System.out.println(123);
        //    inst.addTransformer(new AgentMain());
    }


    //运行后代理
    public static void agentmain(String agentArgs, Instrumentation inst) throws Exception {
        System.out.println("加载 aa");
        Class[] allLoadedClasses = inst.getAllLoadedClasses();
        // inst.addTransformer(new AgentMain());
        // String target = "org.pyq.agent.Application";
        String target = "com.bocloud.cmp.boot.interceptor.AccessInterceptor";
        // String target = "org.pyq.agent.AccessInterceptor";
        Class clazz = null;
        ClassLoader classLoader = null;
        for (Class allLoadedClass : allLoadedClasses) {
            if (allLoadedClass.getName().equals(target)) {
                clazz = allLoadedClass;
                classLoader = allLoadedClass.getClassLoader();
            }
        }

        System.out.println(classLoader.toString());
        //byte[] servlet = servlet(classLoader, target);
          byte[] servlet = servletByBytekit2(clazz);
        //byte[] servlet = removeCode1(classLoader,target);
        ClassDefinition classDefinition = new ClassDefinition(clazz, servlet);
        inst.redefineClasses(classDefinition);
        System.out.println("aa  agent 运行完成");
    }

    private static byte[] servletByBytekit2(Class clazz) throws Exception {
        ClassNode classNode1 = AsmUtils.loadClass(clazz);

        //原始
        ClassNode classNode_original = AsmUtils.copy(classNode1);

        RandomAccessFile f = new RandomAccessFile("E:\\idea-project-corporation-bocloud\\yanghang\\bocloud.ims\\bocloud.ims.booter\\target\\classes\\com\\bocloud\\cmp\\boot\\interceptor\\AccessInterceptor.class", "r");
        final byte[] bytes = new byte[(int) f.length()];
        f.readFully(bytes);
        String className = Type.getObjectType(classNode1.name).getClassName();
        byte[] bytes2 = AsmUtils.renameClass(bytes, className);
        //增强
        ClassNode classNode_enhance = AsmUtils.toClassNode(bytes2);

        for (MethodNode method_enhance : classNode_enhance.methods) {
            if (method_enhance.name.equals("handleExternal")) {

                AsmUtils.replaceMethod(classNode_original, method_enhance);
            }else if (method_enhance.name.equals("permissionValidation")) {

                AsmUtils.replaceMethod(classNode_original, method_enhance);
            }
        }

        System.err.println(Decompiler.toString(classNode_original));

        return AsmUtils.toBytes(classNode_original);

    }

    private static byte[] servletByBytekit1(ClassLoader classLoader, String target, Class clazz) throws Exception {
        try {
            apmClassNode = AsmUtils.loadClass(MyInstrument.class);
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            originClassNode = AsmUtils.loadClass(clazz);
        } catch (IOException e) {
            e.printStackTrace();
        }

        byte[] renameClass = AsmUtils.renameClass(AsmUtils.toBytes(apmClassNode),
                Type.getObjectType(originClassNode.name).getClassName());

        apmClassNode = AsmUtils.toClassNode(renameClass);

        targetClassNode = AsmUtils.copy(originClassNode);

        return replace("a");

    }

    private static byte[] replace(String methodName) throws Exception {
        System.err.println(methodName);
        for (MethodNode methodNode : apmClassNode.methods) {
            if (methodNode.name.equals(methodName)) {
                methodNode = AsmUtils.removeLineNumbers(methodNode);
                // 从原来的类里查找对应的函数
                MethodNode findMethod = AsmUtils.findMethod(originClassNode.methods, methodNode);
                if (findMethod != null) {
                    MethodNode methodNode2 = InstrumentImpl.replaceInvokeOrigin(originClassNode.name, findMethod,
                            methodNode);

                    System.err.println(Decompiler.toString(methodNode2));

                    AsmUtils.replaceMethod(targetClassNode, methodNode2);

                } else {

                }
            }
        }

        byte[] resutlBytes = AsmUtils.toBytes(targetClassNode);

        return resutlBytes;
//        System.err.println("=================");
//
//        System.err.println(Decompiler.decompile(resutlBytes));
//
//        // System.err.println(AsmUtils.toASMCode(resutlBytes));
//
//        VerifyUtils.asmVerify(resutlBytes);
//        return VerifyUtils.instanceVerity(resutlBytes);
    }

    private static byte[] servletByBytekit(ClassLoader classLoader, String target, Class clazz) throws Exception {
        //AgentUtils.install();
        // 解析定义的 Interceptor类 和相关的注解
        DefaultInterceptorClassParser interceptorClassParser = new DefaultInterceptorClassParser();
        List<InterceptorProcessor> processors = interceptorClassParser.parse(SampleInterceptor.class);

        // 加载字节码
        ClassNode classNode = AsmUtils.loadClass(clazz);
        // 对加载到的字节码做增强处理
        for (MethodNode methodNode : classNode.methods) {
            if (methodNode.name.equals("a")) {
                MethodProcessor methodProcessor = new MethodProcessor(classNode, methodNode);
                for (InterceptorProcessor interceptor : processors) {
                    interceptor.process(methodProcessor);
                }
            }
        }

        // 获取增强后的字节码
        byte[] bytes = AsmUtils.toBytes(classNode);
        // 查看反编译结果
        //System.out.println("反编译："+Decompiler.decompile(bytes));

        return bytes;
    }


    @Override
    public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {

        String target = "javax/servlet/http/HttpServlet";
        if (className.contains("HttpServlet")) {
            System.out.println(className);
        }
        if (!className.equals(target)) {
            return null;
        }
        byte[] servlet = new byte[0];
        try {
            servlet = servlet(loader, target);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return servlet;
    }

    public void addMethod(CtClass returnType, String mName, CtClass ctClass) {
        CtMethod ctMethod = new CtMethod(ctClass, mName, new CtClass[]{}, ctClass);
        //公开方法
        ctMethod.setModifiers(Modifier.PUBLIC);

        try {
            ctMethod.setBody("{return registrar}");
            ctClass.addMethod(ctMethod);
        } catch (CannotCompileException e) {
            e.printStackTrace();
        }

    }


    public static byte[] removeCode1(ClassLoader loader, String target) throws Exception {
        ClassPool classPool = new ClassPool();
        LoaderClassPath loaderClassPath = new LoaderClassPath(loader);
        classPool.insertClassPath(loaderClassPath);

        String replace = target.replace("/", ".");
        CtClass cla = classPool.get(replace);

        CtClass[] params = {
                classPool.get("javax.servlet.http.HttpServletRequest"),
                classPool.get("javax.servlet.http.HttpServletResponse")
        };
        CtMethod ctMethod = cla.getDeclaredMethod("handleExternal", params);

        MethodInfo methodInfo = ctMethod.getMethodInfo();
        System.out.println("handleExternal行号:" + methodInfo.getLineNumber(0));
        CodeAttribute codeAttribute = ctMethod.getMethodInfo().getCodeAttribute();
        LineNumberAttribute lineNumberTable = (LineNumberAttribute) codeAttribute.getAttribute("LineNumberTable");
        int startPc = lineNumberTable.toStartPc(122);
        int endPc = lineNumberTable.toStartPc(125);

        byte[] code = codeAttribute.getCode();

        for (int i = startPc; i < endPc; i++) {
            code[i] = CodeAttribute.NOP;
        }
        //ctMethod.insertBefore(src);
        byte[] bytes = cla.toBytecode();
        classPool.removeClassPath(loaderClassPath);
        return bytes;
    }

    public static byte[] removeCode(ClassLoader loader, String target) throws Exception {
        ClassPool classPool = new ClassPool();
        LoaderClassPath loaderClassPath = new LoaderClassPath(loader);
        classPool.insertClassPath(loaderClassPath);

        String replace = target.replace("/", ".");
        CtClass cla = classPool.get(replace);
        CtClass[] params = {
                classPool.get("java.lang.String")
        };
        CtMethod ctMethod = cla.getDeclaredMethod("a", params);

        CodeAttribute codeAttribute = ctMethod.getMethodInfo().getCodeAttribute();
        LineNumberAttribute lineNumberTable = (LineNumberAttribute) codeAttribute.getAttribute("LineNumberTable");
        int startPc = lineNumberTable.toStartPc(38);
        int endPc = lineNumberTable.toStartPc(39);

        byte[] code = codeAttribute.getCode();

        for (int i = startPc; i < endPc; i++) {
            code[i] = CodeAttribute.NOP;
        }
        //ctMethod.insertBefore(src);
        byte[] bytes = cla.toBytecode();
        classPool.removeClassPath(loaderClassPath);
        return bytes;
    }


    public static byte[] servlet(ClassLoader loader, String target) throws Exception {

        ClassPool classPool = new ClassPool();
        LoaderClassPath loaderClassPath = new LoaderClassPath(loader);
        classPool.insertClassPath(loaderClassPath);

        String replace = target.replace("/", ".");
        CtClass cla = classPool.get(replace);
        CtClass[] params = {
                classPool.get("java.lang.String")
        };
        CtMethod ctMethod = cla.getDeclaredMethod("a", params);

        //ctMethod.insertBefore("org.pyq.agent.Demo.begin($$)");
        String src = "{System.out.println(\"你好啊444！！！！！\");" +
                "demoService.demo();" +
                "return \"s -- \"+$1;" +
                "}";
        ctMethod.setBody(src);

        //  并不能引用局部变量
        //  ctMethod.insertAfter("s = \"nihao\";");


        //ctMethod.insertBefore(src);
        byte[] bytes = cla.toBytecode();
        classPool.removeClassPath(loaderClassPath);
        return bytes;
    }

    //创建线程
    public static void createThread() throws Exception {
        ClassPool classPool = new ClassPool(true);
        classPool.insertClassPath(new LoaderClassPath(AgentMain.class.getClassLoader()));
        CtClass ctClass = classPool.get("org.pyq.agent.AgentMain$A");

        Class<?> aClass = ctClass.toClass();
        Object o = aClass.newInstance();
        Thread.sleep(999999);
    }

    public static void createClass() throws Exception {

        ClassPool classPool = new ClassPool(true);
        classPool.insertClassPath(new LoaderClassPath(AgentMain.class.getClassLoader()));

        //构建一个新的CtClass对象
        CtClass ctClass = classPool.makeClass("com.pyq.hello");
        //添加接口
        ctClass.addInterface(classPool.get(IHello.class.getName()));
        //返回值
        CtClass returnType = classPool.get(void.class.getName());
        //参数
        CtClass[] parameters = {classPool.get(String.class.getName())};
        //方法名
        String methodName = "sayHello";
        CtMethod ctMethod = new CtMethod(returnType, methodName, parameters, ctClass);

        String src = "{" +
                "" +
                "System.out.println(\"hello \"+$1);" +
                "" +
                "}";
        ctMethod.setBody(src);

        ctClass.addMethod(ctMethod);

        Class<?> aClass = ctClass.toClass();

        IHello o = (IHello) aClass.newInstance();

        o.sayHello("aaa");

        ctClass.writeFile("E:\\IdeaProjects-git\\hot-update-code\\agent\\src\\main\\java\\org\\pyq\\agent\\");
    }


    public interface IHello {
        public void sayHello(String name);
    }

    public static class A implements Runnable {

        public A() {
            Thread thread = new Thread(this);
            thread.start();
            System.out.println(9999);
        }

        @Override
        public void run() {
            while (true) {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("植入的线程");
            }
        }
    }
}
